package co.yixiang.common.aspect;

import co.yixiang.api.ApiResult;
import co.yixiang.common.aop.NoRepeatSubmit;
import co.yixiang.common.util.RedisLock;
import co.yixiang.common.util.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

/**
 * 重复提交aop
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {

    @Autowired
    private RedisLock redisLock;

    /**
     * 切入点定义，匹配使用了 @NoRepeatSubmit 注解的方法
     *
     * @param noRepeatSubmit 用于获取注解参数
     */
    @Pointcut("@annotation(noRepeatSubmit)")
    public void pointCut(NoRepeatSubmit noRepeatSubmit) {
    }

    /**
     * 环绕通知，用于处理重复提交的请求
     *
     * @param pjp            切入点
     * @param noRepeatSubmit 注解，用于获取锁时长等参数
     * @return 执行结果或重复请求的错误信息
     * @throws Throwable
     */
    @Around("pointCut(noRepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
        // 获取锁的秒数
        int lockSeconds = noRepeatSubmit.lockTime();

        // 获取当前请求
        HttpServletRequest request = RequestUtils.getRequest();
        // 确保请求不为空
        Assert.notNull(request, "request can not null");

        // 提取Authorization头中的Token
        String bearerToken = request.getHeader("Authorization");
        String[] tokens = bearerToken.split(" ");
        String token = tokens[1];
        // 获取请求路径
        String path = request.getServletPath();
        // 生成Redis锁的键
        String key = getKey(token, path);
        // 生成客户端ID
        String clientId = getClientId();

        // 尝试获取锁
        boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);
        // 记录尝试获取锁的信息
        log.info("tryLock key = [{}], clientId = [{}]", key, clientId);

        if (isSuccess) {
            // 获取锁成功
            log.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
            Object result;

            try {
                // 执行目标方法
                result = pjp.proceed();
            } finally {
                // 释放锁
                redisLock.releaseLock(key, clientId);
                log.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
            }

            return result;

        } else {
            // 获取锁失败，认为是重复提交的请求
            log.info("tryLock fail, key = [{}]", key);
            return ApiResult.fail("重复请求，请稍后再试");
        }

    }

    /**
     * 生成Redis锁的键
     *
     * @param token 用户Token
     * @param path  请求路径
     * @return 锁的键
     */
    private String getKey(String token, String path) {
        return token + path;
    }

    /**
     * 生成客户端ID
     *
     * @return 客户端ID
     */
    private String getClientId() {
        return UUID.randomUUID().toString();
    }

}
